package com.fudan.adse.service.impl;

import com.fudan.adse.component.GrpcServiceStub;
import com.fudan.adse.domain.bo.RowPieceBO;
import com.fudan.adse.domain.cont.CheckStatus;
import com.fudan.adse.domain.cont.OrderStatus;
import com.fudan.adse.domain.cont.TopUpType;
import com.fudan.adse.domain.dto.NewOrderDTO;
import com.fudan.adse.domain.dto.NewTopUpDTO;
import com.fudan.adse.domain.po.*;
import com.fudan.adse.domain.vo.StatisticsVO;
import com.fudan.adse.exception.ApiException;
import com.fudan.adse.mbg.mapper.TicketOrderMapper;
import com.fudan.adse.service.OrderService;
import com.fudan.adse.service.TopUpService;
import com.fudan.adse.service.WalletService;
import com.fudan.adse.utils.UUIDUtil;
import com.github.pagehelper.PageHelper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

import static com.fudan.adse.api.ResultCode.FAILED;

@Slf4j
@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private GrpcServiceStub grpcServiceStub;
    @Autowired
    private TicketOrderMapper orderMapper;
    @Autowired
    private TopUpService topUpService;
    @Autowired
    private WalletService walletService;

    @Override
    public List<TicketOrder> getOrdersByConditions(String username, String status, String checkStatus,
                                                   Integer rowPieceId, Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        TicketOrderExample example = new TicketOrderExample();
        TicketOrderExample.Criteria criteria = example.createCriteria();
        if (!ObjectUtils.isEmpty(username)) {
            criteria.andUsernameEqualTo(username);
        }
        if (status != null) {
            if (Arrays.stream(OrderStatus.values()).anyMatch(s -> s.getStatus().equals(status))) {
                criteria.andStatusEqualTo(status);
            } else {
                throw new ApiException("订单状态不存在");
            }
        }
        if (checkStatus != null) {
            if (Arrays.stream(CheckStatus.values()).anyMatch(s -> s.getStatus().equals(checkStatus))) {
                criteria.andCheckStatusEqualTo(checkStatus);
            } else {
                throw new ApiException("影票状态不存在");
            }
        }
        if (rowPieceId != null) {
            criteria.andRowPieceIdEqualTo(rowPieceId);
        }
        return orderMapper.selectByExample(example);
    }

    @Override
    public TicketOrder addOrder(NewOrderDTO newOrder) {
        TicketOrder order = new TicketOrder();
        BeanUtils.copyProperties(newOrder, order);
        order.setUuid(UUIDUtil.generateUUID(newOrder.toString()));
        // 打九折
        order.setDiscount(0.9f);
        order.setStatus(OrderStatus.UNPAID.getStatus());
        order.setCreateTime(new Date());
        order.setUpdateTime(new Date());
        List<String> seats = Arrays.stream(newOrder.getSeats().split(",")).filter(s -> !s.trim().isEmpty()).collect(Collectors.toList());
        // 票价 * seats 个数 * 折扣
        order.setCosts(BigDecimal.valueOf(seats.size() * newOrder.getPrice() * order.getDiscount()));
        if (orderMapper.insert(order) > 0) {
            grpcServiceStub.updateRemainingCapacity(newOrder.getRowPieceId(), seats.size(), newOrder.getSeats());
            return order;
        }
        throw new ApiException(FAILED);
    }

    @Override
    public Wallet updateOrder(String uuid, String status, String checkStatus) {
        TicketOrder order = new TicketOrder();
        TicketOrder old = getOrderByUuid(uuid);
        // 服务间调用，获取排片场次信息
        RowPieceBO rowPieceBO = grpcServiceStub.getRowPieceById(old.getRowPieceId());
        if (status != null && Arrays.stream(OrderStatus.values()).noneMatch(s -> s.getStatus().equals(status))) {
            throw new ApiException("订单状态不存在");
        } else {
            order.setStatus(status);
            if (OrderStatus.PAID.getStatus().equals(status)) {
                if (rowPieceBO.getEndTime().compareTo(new Date()) <= 0) {
                    throw new ApiException("该场次影片已播放结束，购票失败");
                }
                order.setCheckStatus(CheckStatus.STAY.getStatus());
            } else {
                order.setCheckStatus(null);
            }
        }
        if (checkStatus != null && Arrays.stream(CheckStatus.values()).noneMatch(s -> s.getStatus().equals(checkStatus))) {
            throw new ApiException("影票状态不存在");
        } else {
            order.setCheckStatus(checkStatus);
            if (CheckStatus.REFUND.getStatus().equals(checkStatus)) {
                if (CheckStatus.CHECKED.getStatus().equals(old.getCheckStatus())) {
                    throw new ApiException("该影票已使用，退票失败");
                } else if (rowPieceBO.getStartTime().compareTo(new Date()) < 0) {
                    throw new ApiException("已开始放映，退票失败");
                }
            } else if (CheckStatus.CHECKED.getStatus().equals(checkStatus)) {
                // todo 放映前 10 分钟开始检票
                if (rowPieceBO.getStartTime().compareTo(new Date()) < 0) {
                    throw new ApiException("还未开始放映，检票失败");
                }
            }
        }
        order.setId(old.getId());
        order.setUpdateTime(new Date());
        if (orderMapper.updateByPrimaryKeySelective(order) > 0) {
            Wallet wallet = walletService.getWalletByUsername(old.getUsername());
            if (OrderStatus.UNPAID.getStatus().equals(old.getStatus()) &&
                    OrderStatus.CANCEL.getStatus().equals(status)) {
                // 更新余票
                grpcServiceStub.updateRemainingCapacity(old.getRowPieceId(), -old.getSeats().split(",").length, old.getSeats());
            }
            log.info("更新钱包");
            // 更新钱包
            if (OrderStatus.UNPAID.getStatus().equals(old.getStatus()) &&
                    OrderStatus.PAID.getStatus().equals(status)) {
                // 增加支出
                log.info("计入收入");
                wallet.setOutcome(wallet.getOutcome().add(BigDecimal.valueOf(old.getCosts().doubleValue())));
            } else if (OrderStatus.PAID.getStatus().equals(old.getStatus()) &&
                    CheckStatus.REFUND.getStatus().equals(checkStatus)) {
                // 增加收入
                log.info("计入支出");
                NewTopUpDTO topUpOrder = NewTopUpDTO.builder()
                        .username(old.getUsername())
                        .currency("CNY")
                        .amounts(BigDecimal.valueOf(old.getCosts().doubleValue()))
                        .type(TopUpType.REFUND.getType())
                        .build();
                topUpService.topUp(topUpOrder);
                // 更新余票
                grpcServiceStub.updateRemainingCapacity(old.getRowPieceId(), -old.getSeats().split(",").length, old.getSeats());
                wallet.setIncome(wallet.getIncome().add(BigDecimal.valueOf(old.getCosts().doubleValue())));
            }
            // 更新余额：收 - 支
            log.info("更新余额");
            wallet.setBalance(wallet.getIncome().subtract(wallet.getOutcome()));
            if (walletService.updateByPrimaryKeySelective(wallet) <= 0) {
                log.warn("更新失败");
                throw new ApiException(FAILED);
            }
            return wallet;
        }
        throw new ApiException(FAILED);
    }

    private TicketOrder getOrderByUuid(String uuid) {
        TicketOrderExample example = new TicketOrderExample();
        example.createCriteria().andUuidEqualTo(uuid);
        List<TicketOrder> orders = orderMapper.selectByExample(example);
        if (orders.isEmpty()) {
            throw new ApiException("订单不存在");
        }
        return orders.get(0);
    }

    @Override
    public StatisticsVO getStatisticsDataByConditions(Integer cinemaId, Integer filmId, Integer rowPieceId) {
        List<Integer> rowPieceIds = new ArrayList<>();
        if (cinemaId != null || filmId != null) {
            rowPieceIds.addAll(grpcServiceStub.listRowPiecesByCinemaOrFilm(cinemaId == null ? 0 : cinemaId,
                    filmId == null ? 0 : filmId, "", ""));
        } else if (rowPieceId != null) {
            rowPieceIds.add(rowPieceId);
        }
        List<TicketOrder> orders = new ArrayList<>();
        rowPieceIds.forEach(id -> orders.addAll(getOrdersByConditions(
                null, OrderStatus.PAID.getStatus(), null, id, 1, 1000)));
        StatisticsVO ret = new StatisticsVO();
        ret.initData(orders);
        return ret;
    }
}
